#include <linux/init.h>
#include <linux/percpu.h>
#include <linux/stdlib.h>
#include <asm/sections.h>

static unsigned long __per_cpu_offset[NR_CPUS] = {};

static int __percpu_alloc_areas(void)
{
    unsigned long size;
    unsigned long itemsz;
    void *ptr;

    itemsz = (char*)&__per_cpu_end - (char*)&__per_cpu_start;
    size = itemsz * NR_CPUS;

    ptr = kmalloc(size, GFP_KERNEL);
    if (!ptr)
        return -ENOMEM;

    for (int i = 1; i < NR_CPUS; i++)
    {
        __per_cpu_offset[i] = (unsigned long)ptr - (unsigned long)__per_cpu_start;
        memcpy(ptr, __per_cpu_start, itemsz);
         ptr += itemsz;
    }

    return 0;
}

void __init setup_per_cpu_areas(void)
{
    __percpu_alloc_areas();
}

void* __per_cpu_ptr(void* ptr, int cpu)
{
    return (void*)((char*)(ptr) + __per_cpu_offset[cpu]);
}
